package com.geek.gkpush.framework.mq;

import com.geek.gkpush.util.json.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * <h1>MQ 抽象监听器。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/9/15
 */
@Slf4j
public abstract class AbstractMqListener<T> {

    /**
     * <h2>监听的 Q 名称，该方法仅仅只是用于查看日志使用，方便从日志上看出属于哪个 Q 业务而已，没有其他处理业务逻辑的地方。</h2>
     *
     * @throws
     * @param:
     * @return: java.lang.String
     * @author hmilyylimh
     * @date: 2023/9/23
     */
    protected abstract String getTopicName();

    /**
     * <h2>真实处理接收消息的地方。</h2>
     *
     * @throws
     * @param: msgObject
     * @return: void
     * @author hmilyylimh
     * @date: 2023/9/23
     */
    public abstract void doRecvMsg(T msgObject);

    /**
     * <h2>接收消息的公共逻辑。</h2>
     *
     * @throws
     * @param: recvMsg
     * @return: void
     * @author hmilyylimh
     * @date: 2023/9/23
     */
    protected void recvMsg(String recvMsg) {
        log.info("queue={}, recvMsg={}", getTopicName(), recvMsg);
        // 校验
        if (StringUtils.isBlank(recvMsg)) {
            log.warn("recvMsg is blank.");
            return;
        }

        T msgObject;
        try {
            // 获取当前泛型类，即 AbstractMqListener<T>
            ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();

            // 当前泛型类只有一个参数T, 此type为Class<T>
            Type msgType = parameterizedType.getActualTypeArguments()[0];

            // 将 recvMsg 解析为 T 类型的数据，如果为 String 类型的话，就不用转了，直接使用
            if (String.class.equals(msgType)) {
                msgObject = (T) recvMsg;
            } else {
                msgObject = JacksonUtils.fromJson(recvMsg, msgType);
            }
        } catch (Exception e) {
            String errMsg = String.format("queue=%s, recvMsg format error, recvMsg=%s", getTopicName(), recvMsg);
            log.warn(errMsg, e);
            return;
        }

        doRecvMsg(msgObject);
    }
}